package com.henry.utils;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.henry.dao.entity.SysTplCode;
import com.henry.dao.entity.SysTplPconfig;
import com.henry.dao.mapper.SysGeneratorDao;
import com.henry.dao.mapper.SysTplCodeMapper;
import com.henry.dao.mapper.SysTplPconfigMapper;
import com.henry.utils.dynamic.ClassNameUtils;
import com.henry.utils.dynamic.ColumnEntity;
import com.henry.utils.dynamic.PackageConfig;
import com.henry.utils.dynamic.TableEntity;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Created by 峥桂 on 2017/5/26.
 */
@Component
public class GeneratorCode {

    private VelocityContext context;
    private VelocityEngine ve = new VelocityEngine();
    private final String TEMPLATE_PATH = "src/main/resources/template_ftl";
    private PackageConfig packageConfig;//基础信息配置
    private TableEntity tableEntity;

    private Map<String, String> table;
    private List<Map<String, String>> columns;
    private List<SysTplCode> templatList;


    @Autowired
    private SysGeneratorDao sysGeneratorDao;
    @Autowired
    private SysTplPconfigMapper sysTplPconfigMapper;
    @Autowired
    private SysTplCodeMapper sysTplCodeMapper;

    @PostConstruct
    private void initData() {
        //设置velocity资源加载器
        Properties prop = new Properties();
        prop.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, TEMPLATE_PATH); //此处的fileDir可以直接用绝对路径来
        ve.init(prop);
    }

    /**
     * zip打包生成文件
     *
     * @param zip
     * @param tplType 为null时生成所有的
     */
    public void zipFileDefault(String tableName, ZipOutputStream zip, String tplType) {
        setStrategyConfig(tableName, false);
        for (SysTplCode systplCode : templatList) {
            if (StringUtils.isBlank(tplType)) {
                zipContentByTemplate(zip, systplCode.getTplName());
            } else {
                if ((systplCode.getTplType().contains(tplType))) {
                    zipContentByTemplate(zip, systplCode.getTplName());
                    break;
                } else {
                    throw new RuntimeException(systplCode.getTplType() + "当前系统模板类型与输入模板类型不一致：" + tplType);
                }
            }
        }
    }

    public String yuLan(String tableName, String tplContent) {
        setStrategyConfig(tableName, true);
        File f = ClassNameUtils.buildContentByFile(TEMPLATE_PATH, "", tplContent);
        StringWriter sw = new StringWriter();
        Template tpl = ve.getTemplate(f.getName(), "UTF-8");
        tpl.merge(context, sw);
        return sw.toString();

    }

    public void setPackageConfig(PackageConfig packageConfig) {
        this.packageConfig = packageConfig;
    }

    private void setStrategyConfig(String tableName, boolean isYuLan) {
        this.table = sysGeneratorDao.queryTable(tableName);
        this.columns = sysGeneratorDao.queryColumns(tableName);
        initPackageConfig();
        setTableEntity();
        setVelocityContext();
        if (!isYuLan) {
            initTemplateCode();
        }
    }

    private void initTemplateCode() {
        //查询模板对象
        //并对应的模板内容生成相对应的模板文件
        EntityWrapper ew = new EntityWrapper<SysTplCode>();
        ew.where("channel_type={0}", (StringUtils.isNotBlank(packageConfig.getChannelType()) ? packageConfig.getChannelType() : "DEFAULT_TPL"));
        templatList = sysTplCodeMapper.selectList(ew);
        for (SysTplCode systplCode : templatList) {
            ClassNameUtils.buildContentByFile(TEMPLATE_PATH, systplCode.getTplName(), systplCode.getTplContent());
        }
    }

    /**
     * 将内容加载到模板中追加到zip流中
     *
     * @param zip
     * @param template
     */
    private void zipContentByTemplate(ZipOutputStream zip, String template) {
        //渲染模板
        StringWriter sw = new StringWriter();
        Template tpl = ve.getTemplate(template, "UTF-8");
        tpl.merge(context, sw);
        try {
            String packagename = ClassNameUtils.getFileName(template, tableEntity.getClassName(),
                    packageConfig.getPackageName(), packageConfig.getCustomerName());
            //添加到zip
            zip.putNextEntry(new ZipEntry(packagename));
            IOUtils.write(sw.toString(), zip, "UTF-8");
            IOUtils.closeQuietly(sw);
            zip.closeEntry();
        } catch (IOException e) {
            throw new RuntimeException("渲染模板失败，表名：" + tableEntity.getTableName(), e);
        }
    }

    private void setTableEntity() {
        //表信息
        tableEntity = new TableEntity();
        tableEntity.setTableName(table.get("tableName"));
        tableEntity.setComments(table.get("tableComment"));
        //表名转换成Java类名 packageConfig
        String className = ClassNameUtils.tableToJava(tableEntity.getTableName(), packageConfig.getTablePrefix());
        tableEntity.setClassName(className);
        tableEntity.setClassname(StringUtils.uncapitalize(className));

        //列信息
        ColumnEntity columnEntity;
        List<ColumnEntity> columsList = new ArrayList<>();

        for (Map<String, String> column : columns) {
            columnEntity = new ColumnEntity();
            columnEntity.setDataType(column.get("dataType"));
            columnEntity.setColumnName(column.get("columnName"));
            columnEntity.setComments(column.get("columnComment"));
            columnEntity.setAttrLength(ClassNameUtils.getColunmLength(column.get("columnTypeStr")));
            columnEntity.setNullAble(column.get("nullAble"));
            //列名转换成Java属性名
            String attrName = ClassNameUtils.columnToJava(columnEntity.getColumnName());
            columnEntity.setAttrName(attrName);
            columnEntity.setAttrname(StringUtils.uncapitalize(attrName));
            //列的数据类型，转换成Java类型
            String attrType = ClassNameUtils.dataTypeConvertMap.get(columnEntity.getDataType());
            attrType = (StringUtils.isBlank(attrType) ? "unknowType" : attrType);
            columnEntity.setAttrType(attrType);

            //是否主键
            if ("PRI".equalsIgnoreCase(column.get("columnKey")) && tableEntity.getPk() != null) {
                tableEntity.setPk(columnEntity);
            }
            columsList.add(columnEntity);
        }
        tableEntity.setColumns(columsList);
        //没主键，则第一个字段为主键
        if (tableEntity.getPk() == null) {
            tableEntity.setPk(tableEntity.getColumns().get(0));
        }
    }

    private void setVelocityContext() {
        //封装模板数据
        Map<String, Object> map = new HashMap<>();
        map.put("tableName", tableEntity.getTableName());
        map.put("comments", tableEntity.getComments());
        map.put("pk", tableEntity.getPk());
        map.put("className", tableEntity.getClassName());
        map.put("classname", tableEntity.getClassname());
        map.put("pathName", tableEntity.getClassname().toLowerCase());
        map.put("columns", tableEntity.getColumns());
        map.put("package", packageConfig.getPackageName());
        map.put("customerName", packageConfig.getCustomerName());
        map.put("author", packageConfig.getAuthor());
        map.put("email", packageConfig.getEmail());
        map.put("datetime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
        map.putAll(packageConfig.getMap());
        context = new VelocityContext(map);
    }

    /**
     * 加载包名和作者信息以及基础数据
     */
    private void initPackageConfig() {
        EntityWrapper<SysTplPconfig> ew = new EntityWrapper<SysTplPconfig>();
        List<SysTplPconfig> list = sysTplPconfigMapper.selectList(ew);
        if (list == null || list.size() == 0) {
            return;
        }

        packageConfig = new PackageConfig();
        Map<String,Object> map=new HashMap<>();
        for (SysTplPconfig pconfig : list) {
            if ("customerName".equals(pconfig.getConfkey())) {
                packageConfig.setCustomerName(pconfig.getConfvalue());
            } else if ("package".equals(pconfig.getConfkey())) {
                packageConfig.setPackageName(pconfig.getConfvalue());
            } else if ("author".equals(pconfig.getConfkey())) {
                packageConfig.setAuthor(pconfig.getConfvalue());
            } else if ("tablePrefix".equals(pconfig.getConfkey())) {
                if (StringUtils.isNotBlank(pconfig.getConfvalue())) {
                    packageConfig.setTablePrefix(pconfig.getConfvalue().split(","));
                }
            } else if ("email".equals(pconfig.getConfkey())) {
                packageConfig.setEmail(pconfig.getConfvalue());
            } else if ("channelType".equals(pconfig.getConfkey())) {
                packageConfig.setChannelType(pconfig.getConfvalue());
            }else{
                map.put(pconfig.getConfkey(),pconfig.getConfvalue());
            }
        }
        packageConfig.setMap(map);
    }

    public String converContent(String content) {
        if(StringUtils.isBlank(content)){
            return "";
        }
        content = content.replace("'", "&apos;");//替换单引号
        content = content.replace("\"", "&quot;"); // 替换双引号
        content = content.replaceAll("&", "&amp;");//替换&
        /*content = content.replace("\t", "&nbsp;&nbsp;");// 替换跳格
        content = content.replace(" ", "&nbsp;");// 替换空格*/
        content = content.replace("<", "&lt;");//替换左尖括号
        content = content.replaceAll(">", "&gt;");//替换右尖括号
        return content;
    }

    public String revertConverContent(String revertContent) {
        if(StringUtils.isBlank(revertContent)){
            return "";
        }
        revertContent = revertContent.replaceAll("&amp;", "&");//替换&
        revertContent = revertContent.replace("&apos;", "'");//替换单引号
        revertContent = revertContent.replace("&quot;", "\""); // 替换双引号
//        revertContent = revertContent.replace("&nbsp;&nbsp;", "\t");// 替换跳格
//        revertContent = revertContent.replace("&nbsp;", " ");// 替换空格
        revertContent = revertContent.replace("&lt;", "<");//替换左尖括号
        revertContent = revertContent.replaceAll("&gt;", ">");//替换右尖括号
        return revertContent;
    }

}
